/*
 * Copyright 2011 Vlad Goldman.  All rights reserved.
  
   This file is part of GooOutSync - Google Outlook Sync application
  
    Project page: http://goooutsync.googlecode.com

    GooOutSync is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    GooOutSync is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GooOutSync.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.vgold.goooutsync;

import java.io.IOException;

import javax.swing.SwingUtilities;

import org.apache.log4j.Logger;

import com.google.gdata.util.ServiceException;
import com.vgold.goooutsync.AContact.Fields;

public class GooOutSync {
	private static Logger logger;
	GooutSyncGUI gui;
	GoogleContacts googleContacts;
	OutlookContacts outlookContacts;
	private boolean isInSync;
	private boolean syncFailed;
	private GooOutSyncPreferences preferences;
	private GooOutSyncStatus gooOutSyncStatus;

	public GooOutSync() {
		try {
			logger = Logger.getLogger(GooOutSync.class);
			preferences=GooOutSyncPreferences.getGooOutSyncPreferences();
			gooOutSyncStatus = GooOutSyncStatus.getGooOutSyncStatus();
			logger.info("Creating GUI");
			isInSync = false;
			gui=new GooutSyncGUI(this);
			SwingUtilities.invokeLater(gui);
		} catch (GooOutSyncException e) {
			logger.fatal(e.getMessage());
			e.printStackTrace();
		} finally {
			// gui.destroy();
			// System.exit(0);
		}
	}

	public synchronized boolean isInSync() {
		return isInSync;
	}

	public synchronized void setInSync(boolean value) {
		isInSync = value;
		if (value)
			syncFailed = false;
	}

	public synchronized boolean getSyncFailed() {
		return syncFailed;
	}

	public synchronized void setSyncFailed(boolean value) {
		syncFailed = value;
	}

	/**
	 * @param args
	 * @throws GooOutSyncException
	 */
	public synchronized void retrieveContacts() throws GooOutSyncException {
		gooOutSyncStatus.reset();
		logger.info("Creating Google Contacts");
		googleContacts = new GoogleContacts();
		logger.info("Creating Outlook Contacts");
		outlookContacts = new OutlookContacts();

	}

	public synchronized void cleanContacts() {

		logger.info("Cleaning Google Contacts");
		googleContacts = null;
		logger.info("Cleaning Outlook Contacts");
		outlookContacts = null;

	}

	@SuppressWarnings("unused")
	public static void main(String[] args) {
		GooOutSync gooOutSync = new GooOutSync();
	}

	public int getProxyMode() {
		return 0;
	}

	public synchronized void syncNow() throws ServiceException, IOException,
			GooOutSyncException, InterruptedException {
		logger.info("Starting sync...");
		outlookContacts.resetIterator();
		googleContacts.resetIterator();
		// start with outlook contacts
		logger.info("Working on outlook contacts...");
		while (outlookContacts.hasNext()) {
			OutlookContact oContact = outlookContacts.getNext();
			logger.info("Working on contact TITLE:"
					+ oContact.getField(Fields.TITLE) + " FNAME:"
					+ oContact.getField(Fields.FIRST) + " LNAME:"
					+ oContact.getField(Fields.LAST));
			String outID = oContact.getField(Fields.OUTID);
			// if Outlook contact has Google ID,
			if (oContact.containsKey(Fields.GOOID)) {
				logger.info("Contact has google ID, checking Google list");
				// check if this contact is in Google Contacts list
				String gooID = oContact.get(Fields.GOOID);
				if (googleContacts.containsKey(gooID)) {
					GoogleContact gContact = googleContacts.get(gooID);
					logger.info("Contact found in Google list, starting comparison");
					// Contact found in both lists, start field comparison
					if (!oContact.equals(gContact)) {
						// Contacts are not in sync,One or both contacts
						// modified
						gooOutSyncStatus.incDifferentContacts();
						logger.info("Contacts are different, starting sync");
						oContact.syncWith(gContact);
						if (gContact.isChanged()) {
							logger.info("Modified Google contact, saving.");
							googleContacts.updateGoogleContact(gContact);
							gooOutSyncStatus.incModifiedGoogleContacts();
						}
						if (oContact.isChanged()) {
							logger.info("Modified Outlook contact, saving.");
							outlookContacts.updateOutlookContact(oContact);
							gooOutSyncStatus.incModifiedOutlookContacts();
						}
					}
					// remove from the list synced contacts
					logger.info("Removing processed contacts from the lists");
					outlookContacts.processed(outID);
					googleContacts.processed(gooID);
					gooOutSyncStatus.incSyncedContacts();
					outlookContacts.resetIterator();
					googleContacts.resetIterator();

				} else {
					// Contact was deleted from Google, delete it from Outlook
					logger.warn("Deleting contact "
							+ oContact.getField(Fields.TITLE)
							+ " from Outlook, as it was deleted from Google");
					// TODO
					boolean silentDelete=Boolean.valueOf(preferences.getPreference(
							GooOutSyncPreferences.Prefs.outlook_silent_delete));
					if (!silentDelete) {
						switch (gui.confirmOutlookContactRemoval(oContact
								.getField(Fields.TITLE))) {
						case 2:
							preferences
									.setPreference(
											GooOutSyncPreferences.Prefs.outlook_silent_delete,
											"true");
							logger.debug("Enabled silent delete of Outlook contacts");
						case 0:
							outlookContacts.deleteOutlookContact(oContact);
							gooOutSyncStatus.incDeletedOutloookContacts();
							break;
						case 1:
						case -1:
							// Do not delete if pressed No or closed dialog
							break;

						}
					} else {
						outlookContacts.deleteOutlookContact(oContact);
						gooOutSyncStatus.incDeletedOutloookContacts();
					}

					logger.info("Removing processed contact from Outlook list");
					outlookContacts.processed(outID);
					gooOutSyncStatus.incSyncedContacts();
					outlookContacts.resetIterator();
				}
			} else { // if Outlook contact has NO Google ID, create new google
						// contact
				logger.info("Contact has no Google ID, creating Google contact");
				GoogleContact gContact = googleContacts
						.createGoogleContact(oContact);
				// update outlook contact with GOOGID
				oContact.updateField(Fields.GOOID,
						gContact.getField(Fields.GOOID));
				oContact.save();
				gooOutSyncStatus.incCreatedGoogleContacts();
				logger.info("Google contact created, removing from lists");
				outlookContacts.processed(outID);
				googleContacts.processed(gContact.getField(Fields.GOOID));
				gooOutSyncStatus.incSyncedContacts();
				outlookContacts.resetIterator();
				googleContacts.resetIterator();
			}
		}
		logger.info("Starting with Google contacts");
		while (googleContacts.hasNext()) {
			GoogleContact gContact = googleContacts.getNext();
			logger.info("Working on contact " + gContact.getField(Fields.TITLE));
			String gooID = gContact.getField(Fields.GOOID);
			// if Google contact has Outlook ID
			if (gContact.containsKey(Fields.OUTID)) {
				// check if this contact is in Outlook Contacts list
				String outID = gContact.getField(Fields.OUTID);
				logger.info("Contact has Outlook ID, checking Outlook list");
				if (outlookContacts.containsKey(outID)) {
					// not supposed to happen, it would be taken care of in the
					// first loop
					logger.error("Both contacts found in second loop of sync - BUG");
					OutlookContact oContact = outlookContacts.get(outID);
					// Contact found in both lists, start field comparison
					if (!oContact.equals(gContact)) {
						// Contacts are not in sync,One or both contacts
						// modified
						oContact.syncWith(gContact);
						if (gContact.isChanged()) {
							googleContacts.updateGoogleContact(gContact);
							gooOutSyncStatus.incModifiedGoogleContacts();
						}
						if (oContact.isChanged()) {
							outlookContacts.updateOutlookContact(oContact);
							gooOutSyncStatus.incModifiedOutlookContacts();
						}
					}
					// remove from the list synced contacts
					outlookContacts.processed(outID);
					googleContacts.processed(gooID);
					gooOutSyncStatus.incSyncedContacts();
					outlookContacts.resetIterator();
					googleContacts.resetIterator();

				} else {
					// Contact was deleted from Outlook, delete it from Google
					logger.warn("Deleting contact "
							+ gContact.getField(Fields.TITLE)
							+ " from Google, as it was deleted from Outlook");
					boolean silentDelete=Boolean.valueOf(preferences.getPreference(
							GooOutSyncPreferences.Prefs.google_silent_delete));
					
					if (!silentDelete) {
						switch (gui.confirmGoogleContactRemoval(gContact
								.getField(Fields.TITLE))) {
						case 2:
							preferences
									.setPreference(
											GooOutSyncPreferences.Prefs.google_silent_delete,
											"true");
							logger.debug("Enabled silent delete of Google contacts");
						case 0:
							googleContacts.deleteGoogleContact(gContact);
							gooOutSyncStatus.incDeletedGoogleContacts();
							break;
						case 1:
						case -1:
							// Do not delete if pressed No or closed dialog
							break;

						}
					} else {
						googleContacts.deleteGoogleContact(gContact);
						gooOutSyncStatus.incDeletedGoogleContacts();
					}
					
					logger.info("Removing processed contact from Google list");
					googleContacts.processed(gooID);
					gooOutSyncStatus.incSyncedContacts();
					googleContacts.resetIterator();
				}
			} else { // if Google contact has NO Outlook ID, create new Outlook
						// contact
				logger.info("Contact has no Outlook ID, creating Outlook contact");
				OutlookContact oContact = outlookContacts
						.createOutlookContact(gContact);
				gooOutSyncStatus.incCreatedOutlookContacts();
				logger.info("Outlook contact created, removing from lists");
				outlookContacts.processed(oContact.getOutID());
				googleContacts.processed(gooID);
				gooOutSyncStatus.incSyncedContacts();
				outlookContacts.resetIterator();
				googleContacts.resetIterator();
			}
		}
		logger.debug("Resetting iterators");
		outlookContacts.resetIterator();
		googleContacts.resetIterator();
		if (outlookContacts.hasNext() || googleContacts.hasNext()) {
			logger.debug("Still unprocessed contacts, wrong logic");
			throw new GooOutSyncException(
					"GooOutSync failed sync due to internal logic problem. Please report the issue on the project page.");
		}
		logger.debug("----Statistics----");
		logger.debug("Google Total Contacts: "
				+ gooOutSyncStatus.getGoogleContacts());
		logger.debug("Outlook Total Contacts: "
				+ gooOutSyncStatus.getOutlookContacts());
		logger.debug("Total Different Contacts: "
				+ gooOutSyncStatus.getDifferentContacts());
		logger.debug("Total Synced: " + gooOutSyncStatus.getSyncedContacts());
		logger.debug("Google Created: "
				+ gooOutSyncStatus.getCreatedGoogleContacts());
		logger.debug("Google Modified: "
				+ gooOutSyncStatus.getModifiedGoogleContacts());
		logger.debug("Google Deleted: "
				+ gooOutSyncStatus.getDeletedGoogleContacts());
		logger.debug("Outlook Created: "
				+ gooOutSyncStatus.getCreatedOutlookContacts());
		logger.debug("Outlook Modified: "
				+ gooOutSyncStatus.getModifiedOutlookContacts());
		logger.debug("Outlook Deleted: "
				+ gooOutSyncStatus.getDeletedOutlookContacts());
		logger.debug("Nothing left to process, returning...");
		return;
	}

	public void destroy() {
		gui.destroy();
		System.exit(0);
	}

	public void deleteGoogleContactsWithOutlookIDandCleanOutlook()
			throws GooOutSyncException {
		// if (googleContacts.equals(null) || outlookContacts.equals(null)){
		retrieveContacts();
		// }

		googleContacts.deleteAllGoogleContactsWithOutloookID();
		outlookContacts.cleanGoogleIdFromOutlookContacts();
	}

}
